/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.pa165.bookingmanager.services.impl;

import cz.muni.fi.pa165.bookingmanager.entities.Customer;
import cz.muni.fi.pa165.bookingmanager.entities.Hotel;
import cz.muni.fi.pa165.bookingmanager.entities.Room;
import cz.muni.fi.pa165.bookingmanager.dao.HotelManager;
import cz.muni.fi.pa165.bookingmanager.dao.ReservationManager;
import cz.muni.fi.pa165.bookingmanager.dao.RoomManager;
import cz.muni.fi.pa165.bookingmanager.entities.Reservation;
import cz.muni.fi.pa165.bookingmanager.interceptors.LoggingInterceptor;
import cz.muni.fi.pa165.bookingmanager.services.BookingService;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;

/**
 *
 * @author Jiří Holuša
 */
@Stateless
public class BookingServiceImpl implements BookingService {

    @EJB
    private HotelManager hotelManager;
    @EJB
    private ReservationManager reservationManager;
    @EJB
    private RoomManager roomManager;

    @Override
    @Interceptors(LoggingInterceptor.class)
    public List<Room> findAllFreeRooms(Hotel hotel, Date startTime, int duration) {
        if (hotel == null || hotel.getId() == null) {
            throw new IllegalArgumentException("Hotel nor hotel id cant be null.");
        }
        List<Room> allFreeRooms = roomManager.findAllHotelRooms(hotel.getId());

        Date end = new Date(startTime.getTime() + (duration * 24 * 60 * 60 * 1000));

        List<Reservation> allRes = reservationManager.findAllHotelReservations(hotel.getId(), startTime);

        for (Reservation res : allRes) {
            if (res.getStartDate().equals(startTime)) {
                allFreeRooms.remove(res.getRoom());
            }

            Date endRes = new Date(res.getStartDate().getTime() + ((res.getDuration()) * 24 * 60 * 60 * 1000));

            if (res.getStartDate().after(startTime) && res.getStartDate().before(end)) {
                allFreeRooms.remove(res.getRoom());
            }
        }
        return allFreeRooms;
    }

    @Override
    @Interceptors(LoggingInterceptor.class)
    public List<Customer> findAllCurrentCustomersInHotel(Hotel hotel, Date startTime, int duration) {
        if (hotel == null || hotel.getId() == null) {
            throw new IllegalArgumentException("Hotel nor hotel id can be null.");
        }

        List<Customer> currentCustomersInHotel = new ArrayList<Customer>();
        //endTime - the later bound of the searching interval, eg. interval = <startTime, endTime>
        Date endTime = new Date(startTime.getTime() + (duration * 24 * 60 * 60 * 1000));

        List<Reservation> allReservations = reservationManager.findAllHotelReservations(hotel.getId());
        for (Reservation reservation : allReservations) {
            Date reserStartDate = reservation.getStartDate(); //just a shortcut
            //reserEndDate - the time of the end of the reservation, eg. reservation = <reserStartDate, reserEndDate>
            Date reserEndDate = new Date(reserStartDate.getTime() + (reservation.getDuration() * 24 * 60 * 60 * 1000));

            //customers started before we're searching but they're still in our interval
            if (reserStartDate.before(startTime) && reserEndDate.after(startTime)) {
                currentCustomersInHotel.addAll(reservation.getCustomers());
            } //customers started at exactly the same time we're seaching
            else if (reserStartDate.equals(startTime)) {
                currentCustomersInHotel.addAll(reservation.getCustomers());
            } //customers started later then startTime but they ended before endTime
            else if (reserStartDate.after(startTime) && reserStartDate.before(endTime)) {
                currentCustomersInHotel.addAll(reservation.getCustomers());
            }
        }

        return currentCustomersInHotel;
    }

    @Override
    @Interceptors(LoggingInterceptor.class)
    public List<Hotel> searchHotelByConditions(String city, BigDecimal maxPrice, int minCapacity) {
        List<Hotel> allhotels = hotelManager.findAllHotels();
        List<Hotel> hotelsByConditions = new ArrayList<Hotel>();

        if (city == null && maxPrice == null && minCapacity == 0) {
            return allhotels;
        }

        if (maxPrice != null) {
            if (maxPrice.compareTo(BigDecimal.ZERO) <= 0 || minCapacity < 0) {
                throw new IllegalArgumentException("Price or capacity cannot be less than 0");
            }
        }

        if (city == null && maxPrice != null && minCapacity == 0) {
            List<Hotel> hotelsByPrice = new ArrayList<Hotel>();
            for (Hotel h : allhotels) {
                if (!(roomManager.findRoomsByPrice(BigDecimal.ZERO, maxPrice, h.getId()).isEmpty())) {
                    hotelsByPrice.add(h);
                }
            }
            return hotelsByPrice;
        }

        if (city == null && maxPrice == null && minCapacity != 0) {
            List<Hotel> hotelsByCapacity = new ArrayList<Hotel>();
            for (Hotel h : allhotels) {
                if (!(roomManager.findRoomsByCapacity(minCapacity, h.getId()).isEmpty())) {
                    hotelsByCapacity.add(h);
                }
            }
            return hotelsByCapacity;
        }

        if (city == null && maxPrice != null && minCapacity != 0) {
            for (Hotel h : allhotels) {
                if (!((roomManager.findRoomsByCapacity(minCapacity, h.getId()).isEmpty())
                        || (roomManager.findRoomsByPrice(BigDecimal.ZERO, maxPrice, h.getId()).isEmpty()))) {
                    hotelsByConditions.add(h);
                }
            }
            return hotelsByConditions;
        }

        List<Hotel> hotelsByCity = hotelManager.findHotelsByCity(city);

        if (city != null && maxPrice == null && minCapacity == 0) {
            return hotelsByCity;
        }

        if (city != null && maxPrice != null && minCapacity == 0) {
            for (Hotel h : hotelsByCity) {
                if (!(roomManager.findRoomsByPrice(BigDecimal.ZERO, maxPrice, h.getId()).isEmpty())) {
                    hotelsByConditions.add(h);
                }
            }
            return hotelsByConditions;
        }

        if (city != null && maxPrice == null && minCapacity != 0) {
            for (Hotel h : hotelsByCity) {
                if (!(roomManager.findRoomsByCapacity(minCapacity, h.getId()).isEmpty())) {
                    hotelsByConditions.add(h);
                }
            }
            return hotelsByConditions;
        }

        if (city != null && maxPrice != null && minCapacity != 0) {
            for (Hotel h : hotelsByCity) {
                if (!((roomManager.findRoomsByCapacity(minCapacity, h.getId()).isEmpty())
                        || (roomManager.findRoomsByPrice(BigDecimal.ZERO, maxPrice, h.getId()).isEmpty()))) {
                    hotelsByConditions.add(h);
                }
            }
            return hotelsByConditions;
        }

        return hotelsByConditions;
    }

    @Override
    @Interceptors(LoggingInterceptor.class)
    public List<Room> searchRoomInHotel(Hotel hotel, int capacity, BigDecimal maxPrice) {
        searchRoomInHotelValidate(hotel, capacity, maxPrice);

        List<Room> roomsByCapacity = roomManager.findRoomsByCapacity(capacity, hotel.getId());
        List<Room> result = new ArrayList<Room>();
        for (Room r : roomsByCapacity) {
            if (r.getPrice().compareTo(maxPrice) != 1) {
                result.add(r);
            }

        }
        return result;

    }

    private void searchRoomInHotelValidate(Hotel hotel, int capacity, BigDecimal maxPrice) {
        if (hotel == null) {
            throw new IllegalArgumentException("hotel is null");
        }
        if (hotelManager.getHotelById(hotel.getId()) == null) {
            throw new IllegalArgumentException("hotel id is not in database");
        }

        if (capacity <= 0) {
            throw new IllegalArgumentException("room capacity cant be 0 or less");
        }
        if (maxPrice == null) {
            throw new IllegalArgumentException("maxPrice iss null");
        }
    }

    public void setHotelManager(HotelManager hotelManager) {
        this.hotelManager = hotelManager;
    }

    public void setReservationManager(ReservationManager reservationManager) {
        this.reservationManager = reservationManager;
    }

    public void setRoomManager(RoomManager roomManager) {
        this.roomManager = roomManager;
    }
}
